/*
* David Farr
* 0629099
* thumb.c
*
* Description: This program takes in two arguments, an input folder and an
* output folder, the input folder should contain jpg files. 
* The program with then implement either a PARALLEL or SERIAL algorithm
* to handle decompression, scaling, cropping and compression of the 
* images back into thumbnails. The images will be saved into an output folder.
*
* Notes:
* The no-pipe option is not working, the program will still accept this
* argument but will not implement a pipe-less version.
* The file are not being manipulated properly, the images will be 
* re-sized and re-saved but they are not viewable.
* When you use SERIAL MODE the thumbnails will always be saved in the
* same folder as the input.
*
*/

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include "pilot.h"
#include "jpegutil.h"

#define MAX_P 100
PI_PROCESS *C;
PI_PROCESS *D[MAX_P];
PI_CHANNEL *fromC;
PI_CHANNEL *toD[MAX_P];
PI_CHANNEL *fromD[MAX_P];
PI_BUNDLE *check;

unsigned char *decompress (FILE *file, int *retH, int *retW, int *retB)
{
	unsigned char *buffer, *scaled;
	int height, width, bpp;
	long size;
	int startH, endH, startW, endW, h, w, b;
	
	buffer = readJPEG(file, &height, &width, &bpp);
	
	startH = (height / 100) * 32;
        while (startH % 3 != 0) {
                startH++;
        }
	endH = startH * 2;
	
	startW = (width / 100) * 32;
	while (startW % 3 != 0) {
		startW++;
	}
	endW = startW * 2;	

	/* set some return values */
	*retH = (endH - startH) / 3;
	*retW = (endW - startW) / 3;
	*retB = bpp;
	
	size = (((endH - startH) * (endW - startW)) / 3) * bpp;
	scaled = malloc(sizeof(unsigned char) * size);
	
	int pos = 0;
	for(h = startH; h + (bpp-1) < endH; h += (bpp*3)) {
	
		for(w = startW; w + (bpp-1) < endW; w += (bpp*3)) {
		
			for(b = 0; b < bpp; b++) {
				scaled[pos] = buffer[h + w + b];
				pos++;
			}
		}
	}

	
	free(buffer);
	return scaled;
}

int decompressor (int index, void* arg2)
{
	printf("Start decompressor %d.\n", index);

	FILE *file;
	int nameSize;
	long fileSize;
	char *fileName;
	unsigned char *buffer, *fileStr;
	
	while(1) {
		PI_Read(toD[index], "%d", &nameSize);
		
		/* kill the decompressor if input is -1 */
		if (nameSize == -1) {
			PI_Write(fromD[index], "%d", nameSize);
			break;
		}
		
		/* read in the file name */
		fileName = malloc(sizeof(char) * nameSize);
		PI_Read(toD[index], "%*c", sizeof(char) * nameSize, fileName);
		
		/* read in the file itself */
		PI_Read(toD[index], "%ld", &fileSize);
		fileStr = malloc(sizeof(unsigned char) * fileSize);
		PI_Read(toD[index], "%*hhu", sizeof(unsigned char) * fileSize, fileStr);		
			
		int height, width, bpp;
		long size;
		
		file = fmemopen(fileStr, fileSize, "rb");
		//buffer = readJPEG(file, &height, &width, &bpp);
		buffer = decompress(file, &height, &width, &bpp);
		fclose(file);
		
		/* write to compressor */
		PI_Write(fromD[index], "%d", nameSize);
		PI_Write(fromD[index], "%*c", sizeof(char) * nameSize, fileName);
		PI_Write(fromD[index], "%d", height);
		PI_Write(fromD[index], "%d", width);
		PI_Write(fromD[index], "%d", bpp);

		size = height * width * bpp;		
		PI_Write(fromD[index], "%*hhu", sizeof(unsigned char) * size, buffer);
		
		free(fileStr);
		free(fileName);
		free(buffer);
	}

	printf("End decompressor %d.\n", index);
	
	return 0;
}

void compress(char *pic, unsigned char *scaled, int height, int width, int bpp)
{
	FILE *file;
        size_t s = sizeof(char) * (height * width * bpp);

        file = open_memstream(&pic, &s);
        writeJPEG(file, scaled, height, width, bpp);
	fclose(file);
}

int compressor (int numDs, void *arg2)
{
	printf("Start compressor.\n");

	int c, count = 0;
	int nameSize, height, width, bpp;
	long size;
	char *fileName;
	unsigned char *scaled;
	
	while(1) {
	
		c = PI_Select(check);
		PI_Read(fromD[c], "%d", &nameSize);
		
		if (nameSize == -1) {
			count++;
			if (count == numDs)
				break;
				
			continue;
		}
		
		/* read info from decompressor */		
		fileName = malloc(sizeof(char) * nameSize);
		PI_Read(fromD[c], "%*c", sizeof(char) * nameSize, fileName);
		
		PI_Read(fromD[c], "%d", &height);
		PI_Read(fromD[c], "%d", &width);
		PI_Read(fromD[c], "%d", &bpp);
		
		size = height * width * bpp;
		scaled = malloc(sizeof(unsigned char) * size);
		PI_Read(fromD[c], "%*hhu", sizeof(unsigned char) * size, scaled);	
	
		FILE *file;
		long fileLen;
		char *pic = malloc(sizeof(char) * size);
        	size_t s = sizeof(char) * (height * width * bpp);

        	file = open_memstream(&pic, &s);
		//compress(pic, scaled, height, width, bpp);
        	writeJPEG(file, scaled, height, width, bpp);

		fseek(file, 0, SEEK_END);
                fileLen = ftell(file);
                fseek(file, 0, SEEK_SET);
		
        	fclose(file);
		
		/* pass information to MAIN */
		PI_Write(fromC, "%d", nameSize);
		PI_Write(fromC, "%*c", nameSize, fileName);
		PI_Write(fromC, "%ld", fileLen);
		PI_Write(fromC, "%*c", sizeof(char) * fileLen, pic);
		
		free(fileName);
		free(scaled);
		free(pic);
	}

	printf("End compressor.\n");
	
	return 0;
}

int main(int argc, char *argv[])
{
	int NO_PIPE = 0;
	int SERIAL_MODE = 0;
	char *inputDir, *outputDir;
	double time;
	
	int P = 0;
	P = PI_Configure(&argc, &argv);
	
	/* set serial mode to true in event of one process */
	if (P == 1)
		SERIAL_MODE = 1;
	else if (P == 2) {
		printf("For Part 1 there cannot be only 2 processes.\n");
		exit(1);
	}
	
	/* if there are an improper number of arguments */
	if (argc < 3 || argc > 4) {
		
		printf("Proper use:\n");
		printf("sqsub ... thumb [-nopipe] input-dir output-dir [Pilot options]\n");
		exit(1);
	}
	
	/* if the no-pipe argument is included */
	if (argc == 4) {
		if (strcmp(argv[1], "-nopipe") == 0)
			NO_PIPE = 1;
		inputDir = malloc(sizeof(char) * strlen(argv[2]));
		outputDir = malloc(sizeof(char) * strlen(argv[3]));
		strcpy(inputDir, argv[2]);
		strcpy(outputDir, argv[3]);
	}
	
	/* if the no-pipe argument is not included */
	else {
		inputDir = malloc(sizeof(char) * strlen(argv[1]));
		outputDir = malloc(sizeof(char) * strlen(argv[2]));
		strcpy(inputDir, argv[1]);
		strcpy(outputDir, argv[2]);
	}
	
	/* open up the directory */
	struct dirent *item;
	DIR *in, *out;
	int numFiles = 0, target = 0;
	
	in = opendir(inputDir);
	if (in == NULL) {
		exit(1);
	}
	
	/* count the number of files in the dir */
	while (item = readdir(in)) {
		numFiles++;
		//printf("item: %s\n", item->d_name);
	}
	rewinddir(in);
	
	/* exit if there are no images */
	if (numFiles == 0) {
		printf("There are no images in the input folder specified.\n");
		exit(1);
	}
	
	int i, numDs, complete = 0;
	
	if (SERIAL_MODE == 1)
		numDs = 0;
	else if (numFiles < P-2)
		numDs = numFiles;
	else
		numDs = P - 2;
	
	//printf("Number of files: %d\n", numFiles);
	//printf("Number of decompressors: %d\n", numDs);
	
	/* set up processes and communication */
	if (SERIAL_MODE != 1) {
		C = PI_CreateProcess(compressor, numDs, NULL);
		fromC = PI_CreateChannel(C, PI_MAIN);
	}
	
	for (i = 0; i < numDs; i++) {
		D[i] = PI_CreateProcess(decompressor, i, NULL);
		toD[i] = PI_CreateChannel(PI_MAIN, D[i]);
		fromD[i] = PI_CreateChannel(D[i], C);

		/*if (NO_PIPE == 1)
			fromD[i] = PI_CreateChannel(D[i], PI_MAIN);
		else
			fromD[i] = PI_CreateChannel(D[i], C);*/
	}
	
	/* create the bundle */
	if (SERIAL_MODE != 1)
		check = PI_CreateBundle(PI_SELECT, fromD, numDs);
	
	PI_StartAll();
	PI_StartTime();

	printf("Start MAIN.\n");
	
	FILE *file;
	unsigned char *buffer;
	long fileLen;
	int curD;

	/* some directory handling */
	char *newIn, *newOut;
	newIn = malloc(sizeof(char) * (strlen(inputDir) + 3));
	newOut = malloc(sizeof(char) * (strlen(outputDir) + 3));
	
	strcpy(newIn, "./");
	strcat(newIn, inputDir);

	strcpy(newOut, "./");
        strcat(newOut, outputDir);

	chdir(newIn);
	
	/* loop for every file in the input-dir */
	for (i = 0; i < numFiles; i++) {
		
		item = readdir(in);
		
		/* go to next file if current file is not a jpg */
		if (strstr(item->d_name, "jpg") == NULL && strstr(item->d_name, "jpeg") == NULL) {
			continue;
		}
		
		/* open the file */
		file = fopen(item->d_name, "rb");
		if (!file) {
			continue;
		}
		
		fseek(file, 0, SEEK_END);
		fileLen = ftell(file);
		fseek(file, 0, SEEK_SET);
		
		buffer = malloc(sizeof(unsigned char) * (fileLen + 1));
		if (!buffer) {
			fclose(file);
			continue;
		}
		
		/* read in the file */
		fread(buffer, fileLen, sizeof(unsigned char), file);
		fclose(file);
	
		if (SERIAL_MODE != 1) {
		/* write informations to Decompressor */
			curD = i % (numDs - 1);
			PI_Write(toD[curD], "%d", strlen(item->d_name) + 1); //size of filename
			PI_Write(toD[curD], "%*c", strlen(item->d_name) + 1, item->d_name); //the filename
			PI_Write(toD[curD], "%ld", fileLen); //size of the file
			PI_Write(toD[curD], "%*c", fileLen, buffer); //the file
		}
		else {

			char *pic;
			unsigned char *scaled;
			int height, width, bpp;
			long len;

			file = fmemopen(buffer, fileLen, "rb"); 
        	        scaled = decompress(file, &height, &width, &bpp);
                	fclose(file);

			size_t s = sizeof(char) * (height * width * bpp);
			file = open_memstream(&pic, &s);
                	writeJPEG(file, scaled, height, width, bpp);

        	        fseek(file, 0, SEEK_END);
	                len = ftell(file);
                	fseek(file, 0, SEEK_SET);
        	        fclose(file);

                        /* write to file */
                        int i = 0;
                        char *newName = malloc(sizeof(char) * (strlen(item->d_name) + 4));
                        while(item->d_name[i] != '.') {
                                newName[i] = item->d_name[i];
                                i++;
                        }
                        newName[i] = '\0';
                        strcat(newName, "-tb.jpg");

                        file = fopen(newName, "wb");
                        if (!file) {
                                free(newName);
                    		free(buffer);
                                continue;
                        }

                        fwrite(pic, len, sizeof(char), file);
                        fclose(file);

                        free(newName);
		}

		free(buffer);
		target++;
	}

	if (SERIAL_MODE == 1) {
        	free(newIn);
        	free(newOut);
        	free(inputDir);
        	free(outputDir);

        	printf("Stop MAIN.\n");

		time = PI_EndTime();
		printf("Time to execute: %lf\n", time);

        	PI_StopMain(0);
	        return 0;
	}
	
	mkdir(newOut);
	chdir(newOut);
	
	int c, nameSize;
	long size;
	int count = 0;
	char *fileName;
	unsigned char *pic;
	while (count < target) {
	
		c = PI_ChannelHasData(fromC);
		
		if(c == 1) {
	
			printf("Start image %d.\n", count);
	
			/* read all data from compressor */
			PI_Read(fromC, "%d", &nameSize);
			fileName = malloc(sizeof(char) * nameSize);
			PI_Read(fromC, "%*c", sizeof(char) * nameSize, fileName);			
			PI_Read(fromC, "%ld", &size);
			
			pic = malloc(sizeof(unsigned char) * size);
			PI_Read(fromC, "%*c", sizeof(unsigned char) * size, pic);
			
			/* write to file */
			int i = 0;
			char *newName = malloc(sizeof(char) * (strlen(fileName) + 4));
			while(fileName[i] != '.') {
				newName[i] = fileName[i];
				i++;
			}
			newName[i] = '\0';
			strcat(newName, "-tb.jpg");
 
			file = fopen(newName, "wb");
			if (!file) {
				free(newName);
				free(fileName);
				free(pic);
				count++;
				
				continue;
			}
			
			fwrite(pic, size, sizeof(char), file);
			fclose(file);
		
			free(newName);	
			free(fileName);
			free(pic);

			printf("End image %d.\n", count);
			count++;
		}
	}
	
	/* kill all the decompressors and compressors */
	int kill = -1;
	for (i = 0; i < numDs; i++) {
		PI_Write(toD[i], "%d", kill);
		//PI_Write(toD[i], "%*c", 1, "0");
		//PI_Write(toD[i], "%d", kill);
		//PI_Write(toD[i], "%*c", 1, "0");
	}
	
	free(newIn);
	free(newOut);
	free(inputDir);
	free(outputDir);
	
	printf("Stop MAIN.\n");

	time = PI_EndTime();
        printf("Time to execute: %lf\n", time);

	PI_StopMain(0);
	return 0;
}


